////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2001.
// -------------------------------------------------------------------------
//  File name:   ObjectCreateTool.cpp
//  Version:     v1.00
//  Created:     18/12/2001 by Timur.
//  Compilers:   Visual C++ 6.0
//  Description: 
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "ObjectCreateTool.h"
#include "ObjectTypeBrowser.h"
#include "PanelTreeBrowser.h"
#include "Viewport.h"
#include "DisplaySettings.h"
#include "SegmentedWorld/SegmentedWorldManager.h"

#include "EditMode/ObjectMode.h"
#include "ProceduralSystem/ProceduralCreation.h"

//////////////////////////////////////////////////////////////////////////
IMPLEMENT_DYNCREATE(CObjectCreateTool,CEditTool)

//////////////////////////////////////////////////////////////////////////
CObjectCreateTool::CObjectCreateTool( CreateCallback createCallback )
{
	static const GUID guid = 	{ 0xfc539aa9, 0x9ca0, 0x4db7, { 0xbb, 0xc3, 0xb4, 0x44, 0xf5, 0xff, 0x8c, 0x6b } };
	m_pClassDesc = GetIEditor()->GetClassFactory()->FindClass(guid);

	SetStatusText( "Drag&Drop item to create an object" );

	m_hCreateCursor = AfxGetApp()->LoadCursor( IDC_POINTER_OBJHIT );

	m_pMouseCreateCallback = 0;
	m_createCallback = createCallback;
	m_object = 0;
	m_objectBrowserPanelId = 0;
	m_fileBrowserPanelId = 0;
	m_bStartedUndo = false; 

	m_bCreationPaused = false;
	
	//if (!m_createCallback)
		GetIEditor()->ClearSelection();
}

//////////////////////////////////////////////////////////////////////////
CObjectCreateTool::~CObjectCreateTool()
{
	if (m_pMouseCreateCallback)
		m_pMouseCreateCallback->Release();

	if (m_bStartedUndo && GetIEditor()->IsUndoRecording())
	{
		SW_IGNORE_CHANGES_AUTOGUARD;
		GetIEditor()->CancelUndo();
	}
}

//////////////////////////////////////////////////////////////////////////
void CObjectCreateTool::CloseFileBrowser()
{
	if (m_fileBrowserPanelId)
	{
		GetIEditor()->RemoveRollUpPage(ROLLUP_OBJECTS,m_fileBrowserPanelId);
		m_fileBrowserPanelId = 0;
	}
}

bool CObjectCreateTool::Activate( CEditTool *pPreviousTool ) 
{
	if( pPreviousTool )
	{
		// if we have object mode tool as the previous tool, then we still want to be used when the creation tool is on
		// so we add it as parent, so it will handle mouse actions if we, the create tool, dont handle them
		if( pPreviousTool->IsKindOf( RUNTIME_CLASS( CObjectMode ) ) )
		{
			SetParentTool( pPreviousTool );
		}
	}

	return true; 
};

//////////////////////////////////////////////////////////////////////////
void CObjectCreateTool::SelectCategory( const CString &category )
{
	// Check if this category have more then one subtype.
	std::vector<CString> types;
	GetIEditor()->GetObjectManager()->GetClassTypes( category,types );
	if (types.size() == 1)
	{
		// If only one or less sub types in this category, assume it type itsel, and start creation.
		StartCreation( types[0] );
		return;
	}

	// Check if this category have more then one subtype.
	ObjectTypeBrowser* panel = new ObjectTypeBrowser;
	panel->SetCategory( this,category );
	panel->Create( ObjectTypeBrowser::IDD,AfxGetMainWnd() );

	m_objectBrowserPanelId = GetIEditor()->AddRollUpPage( ROLLUP_OBJECTS,"Object Type",panel );
	AfxGetMainWnd()->SetFocus();
}

//////////////////////////////////////////////////////////////////////////
void CObjectCreateTool::StartCreation( const CString &type,const CString &param )
{
	// Delete object currently in creation.
	if (GetIEditor()->IsUndoRecording())
		GetIEditor()->CancelUndo();

	m_objectType = type;

	CObjectClassDesc *clsDesc = GetIEditor()->GetObjectManager()->FindClass( type );
	if (!clsDesc)
	{
		MessageBox( NULL,"Object creation failed, unknown object type.", "Warning", MB_ICONEXCLAMATION|MB_OK );
		return;
	}
	if (param.IsEmpty())
	{
		CString fileSpec = clsDesc->GetFileSpec();
		if (!fileSpec.IsEmpty())
		{
			//! Check if file spec contain wildcards.
			if (fileSpec.Find("*") >= 0)
			{
				// Create file browser panel.
				// When file is selected OnSelectFile callback will be called and creation process will be finalized.
				CPanelTreeBrowser *br = new CPanelTreeBrowser;
				br->Create( functor(*this,&CObjectCreateTool::OnSelectFile),clsDesc->GetFileSpec(),AfxGetMainWnd() );
				br->SetDragAndDropCallback( functor(*this,&CObjectCreateTool::OnCreateObjectFromFile) );
				if (m_fileBrowserPanelId==0)
					m_fileBrowserPanelId = GetIEditor()->AddRollUpPage( ROLLUP_OBJECTS,"Browser",br );
				br->AddPreviewPanel();
				AfxGetMainWnd()->SetFocus();
				return;
			}
		}
		OnSelectFile( fileSpec );
	}
	else
	{
		OnSelectFile( param );
	}
}

//////////////////////////////////////////////////////////////////////////
void CObjectCreateTool::OnCreateObjectFromFile( CString file )
{
	FileChanged( file,true );
}

//////////////////////////////////////////////////////////////////////////
void CObjectCreateTool::OnSelectFile( CString file )
{
	if (m_object && m_object->GetType() == OBJTYPE_SOLID)
		FileChanged( file,true );
	else
		FileChanged( file,false );
}

//////////////////////////////////////////////////////////////////////////
void CObjectCreateTool::FileChanged( CString file,bool bMakeNew )
{
	if (m_objectType.IsEmpty())
	{
		CancelCreation();
		return;
	}
	m_bCreationPaused = false;
	m_file = file;
	CWaitCursor wait;
	GetIEditor()->BeginUndo();
	m_bStartedUndo = true;

	if ( m_objectType==PREFABBUILDING_OBJECT_CLASS_NAME || m_objectType==PREFAB_OBJECT_CLASS_NAME )
	{
		if (CProceduralCreationWorks::MustCreatePrefabBuilding(file))
			m_objectType=PREFABBUILDING_OBJECT_CLASS_NAME;
		else
			m_objectType=PREFAB_OBJECT_CLASS_NAME;
	}

	Matrix34 objectTM;
	objectTM.SetIdentity();
	if (m_object && !bMakeNew)
	{
		// Delete previous object.
		objectTM = m_object->GetLocalTM();
		GetIEditor()->DeleteObject(m_object);
		m_object = 0;
		m_bCreationPaused = true;
	}

 	m_object = GetIEditor()->NewObject( m_objectType,file );
	if (m_object)
	{
		m_object->SetLocalTM(objectTM);
		// Close file browser if was open, not needed anymore.
		//CloseFileBrowser();

		// if this object type was hidden by category, re-display it.
		int hideMask = GetIEditor()->GetDisplaySettings()->GetObjectHideMask();
		hideMask = hideMask & ~(m_object->GetType());
		GetIEditor()->GetDisplaySettings()->SetObjectHideMask( hideMask );

		// Enable display of current layer.
		CObjectLayer *pLayer = GetIEditor()->GetObjectManager()->GetLayersManager()->GetCurrentLayer();
		pLayer->SetFrozen(false);
		pLayer->SetVisible(true);
		pLayer->SetModified();

		if (!m_createCallback)
			GetIEditor()->GetObjectManager()->BeginEditParams( m_object,OBJECT_CREATE );
		// Document modified.
		GetIEditor()->SetModifiedFlag();
		GetIEditor()->SetModifiedModule(eModifiedBrushes);
	}

	if (m_createCallback)
		m_createCallback( this,m_object );

	if (m_object)
		m_pMouseCreateCallback = m_object->GetMouseCreateCallback();
}

//////////////////////////////////////////////////////////////////////////
void CObjectCreateTool::CancelCreation()
{
	// Make sure created object is unselected.
	if (m_object)
	{
		// Destroy ourself.
		GetIEditor()->SetEditTool(0);
	}
}

//////////////////////////////////////////////////////////////////////////
void CObjectCreateTool::AcceptCreation( bool bAbortTool )
{
	// Make sure created object is unselected.
	if (m_object)
	{
		if (GetIEditor()->IsUndoRecording())
			GetIEditor()->AcceptUndo( CString("New ")+m_object->GetTypeName() );

		m_bCreationPaused = true;
		m_bStartedUndo = false;

		if (bAbortTool)
			Abort();
	}
}

//////////////////////////////////////////////////////////////////////////
bool CObjectCreateTool::MouseCallback( CViewport *view,EMouseEvent event,CPoint &point,int flags )
{
	// Currently only an object of CSolidBrushObject class has its own IMouseCreateCallback object. 
	// We need to process events of mousecallback differently according to whether m_pMouseCreateCallback is NULL or not
	// to improve a way of creating a solid.
	if( m_pMouseCreateCallback == NULL )
		return MouseCallBackWithOutMouseCreateCB( view, event, point, flags );

	return MouseCallBackWithMouseCreateCB( view, event, point, flags );
}


bool CObjectCreateTool::MouseCallBackWithMouseCreateCB( CViewport *view,EMouseEvent event,CPoint &point,int flags )
{
	if( m_pMouseCreateCallback == NULL )
		return false;
		
	bool bCreateNewObject = m_pMouseCreateCallback->ContinueCreation();	

	if (m_object != NULL && !m_bCreationPaused)
	{
		GetIEditor()->SuspendUndo();
		int res = m_pMouseCreateCallback->OnMouseEvent( view,event,point,flags );
		GetIEditor()->ResumeUndo();

		if (res == MOUSECREATE_ABORT)
		{
			CancelCreation();	// Cancel object creation.
		}
		else if (res == MOUSECREATE_OK)
		{
			if (CProceduralCreationWorks::IsProceduralCreationActivated())
			{
				if (GetIEditor()->GetSelectedObject())
					CProceduralCreationWorks::AssignObjectToBuilding(CProcSettings::GetProcSettings().lastBuildingGUID,GetIEditor()->GetSelectedObject()->GetId(),CProcSettings::GetProcSettings().currentFloorNumber);
			}
			AcceptCreation(!bCreateNewObject);
			return true;
		}
		else if ( event == eMouseMove)
		{
			if (CProceduralCreationWorks::IsProceduralCreationActivated())
				CProceduralCreationWorks::FollowFloorZ(view,point);
		}
		
		return true;
	}
	else if (m_bCreationPaused)
	{
		if (event == eMouseLDown)
		{
			if (!m_bStartedUndo)
				GetIEditor()->BeginUndo();

			m_bStartedUndo = true;
			m_bCreationPaused = false;

			if (bCreateNewObject)
				OnSelectFile( m_file );

			m_pMouseCreateCallback->OnMouseEvent( view, event, point, flags );
		
			return true;
		}
	}
	
	// we didnt handled this event, let parent(s) do it (maybe CObjectMode tool)
	return false;
}


bool CObjectCreateTool::MouseCallBackWithOutMouseCreateCB( CViewport *view,EMouseEvent event,CPoint &point,int flags )
{
	if (m_object != NULL && !m_bCreationPaused)
	{
		GetIEditor()->SuspendUndo();
		int res = m_object->MouseCreateCallback( view,event,point,flags );
		GetIEditor()->ResumeUndo();

		if (res == MOUSECREATE_ABORT)
		{
			CancelCreation();	// Cancel object creation.
		}
		else if (res == MOUSECREATE_OK)
		{
			if (CProceduralCreationWorks::IsProceduralCreationActivated())
			{
				if (GetIEditor()->GetSelectedObject())
					CProceduralCreationWorks::AssignObjectToBuilding(CProcSettings::GetProcSettings().lastBuildingGUID,GetIEditor()->GetSelectedObject()->GetId(),CProcSettings::GetProcSettings().currentFloorNumber);
			}
			AcceptCreation(true);
			return true;

		}
		else if ( event == eMouseMove)
		{
			if (CProceduralCreationWorks::IsProceduralCreationActivated())
				CProceduralCreationWorks::FollowFloorZ(view,point);			
		}

		// we handled this event
		return true;
	}
	else if (m_bCreationPaused)
	{
		if (event == eMouseLDown)
		{
			if (!m_bStartedUndo)
				GetIEditor()->BeginUndo();
			m_bStartedUndo = true;
			m_bCreationPaused = false;

			// we handled this event
			return true;
		}
	}

	// we didnt handled this event, let parent(s) do it (maybe CObjectMode tool)
	return false;
}


//////////////////////////////////////////////////////////////////////////
void CObjectCreateTool::BeginEditParams( IEditor *ie,int flags )
{
}

//////////////////////////////////////////////////////////////////////////
void CObjectCreateTool::EndEditParams()
{
	if (m_objectBrowserPanelId)
	{
		GetIEditor()->RemoveRollUpPage(ROLLUP_OBJECTS,m_objectBrowserPanelId);
		m_objectBrowserPanelId = 0;
	}
	CloseFileBrowser();
}

//////////////////////////////////////////////////////////////////////////
bool CObjectCreateTool::OnKeyDown( CViewport *view,uint32 nChar,uint32 nRepCnt,uint32 nFlags )
{ 
	if (nChar == VK_ESCAPE || nChar == VK_DELETE)
	{
		CancelCreation();
	}
	return false;
}

//////////////////////////////////////////////////////////////////////////
bool CObjectCreateTool::OnSetCursor( CViewport *vp )
{
	SetCursor( m_hCreateCursor );
	return true;
}

//////////////////////////////////////////////////////////////////////////
void CObjectCreateTool::SetUserData( const char *key,void *userData )
{
	if (0 == strcmp(key,"category"))
	{
		SelectCategory((const char*)userData);
	}
	else if (0 == strcmp(key,"type"))
	{
		StartCreation((const char*)userData);
	}
}

//////////////////////////////////////////////////////////////////////////
// Class description.
//////////////////////////////////////////////////////////////////////////
class CObjectCreateTool_ClassDesc : public CRefCountClassDesc
{
	//! This method returns an Editor defined GUID describing the class this plugin class is associated with.
	virtual ESystemClassID SystemClassID() { return ESYSTEM_CLASS_EDITTOOL; }

	//! Return the GUID of the class created by plugin.
	virtual REFGUID ClassID() 
	{
		// {FC539AA9-9CA0-4db7-BBC3-B444F5FF8C6B}
		static const GUID guid = 	{ 0xfc539aa9, 0x9ca0, 0x4db7, { 0xbb, 0xc3, 0xb4, 0x44, 0xf5, 0xff, 0x8c, 0x6b } };
		return guid;
	}

	//! This method returns the human readable name of the class.
	virtual const char* ClassName() { return "EditTool.ObjectCreate"; };

	//! This method returns Category of this class, Category is specifing where this plugin class fits best in
	//! create panel.
	virtual const char* Category() { return "Object"; };
	virtual CRuntimeClass* GetRuntimeClass() { return RUNTIME_CLASS(CObjectCreateTool); }
	//////////////////////////////////////////////////////////////////////////
};

REGISTER_CLASS_DESC(CObjectCreateTool_ClassDesc)
